/** @file         i2c_mpu6050_drv.c
 *  @brief        6050驱动文件
 *  @details      基于驱动模块的方式实现 设备注册/注销等等操作。
 *  @author       lzm
 *  @date         2021-06-27 22:20:03
 *  @version      v1.0
 *  @copyright    Copyright By lizhuming, All Rights Reserved
 *  @blog         https://www.cnblogs.com/lizhuming/
 **********************************************************
 *  @LOG 修改日志:
 **********************************************************
*/

#include "i2c_mpu6050_drv.h"

/* 
 * 步骤注释声明
 * [module]：模块框架
 * [drv]：实现驱动内容。包括内核设备文件、设备节点等等。
 * [bus_drv]:总线驱动。包括probe、remove、兼容等等。
 * 总线设备框架和总线驱动框架都是属于内核的架子，和模块框架一个性质。 
 */

/* [drv] 驱动内容 */
static struct class *mpu6050_dev_class = NULL; // 设备类
static int id_num = 0;

/** @brief i2c_mpu6050_write 
  * @details get hardware resource and make i2c write 
  * @param mpu6050_client: i2c 从设备
  * @param address: 需要写入的地址
  * @param data: 需要写入的数据
  * @retval 
  * @author lzm
  */
static int i2c_mpu6050_write(struct i2c_client *mpu6050_client, unsigned char address, unsigned char data)
{
    int ret = 0;
    unsigned char write_data[2];
    struct i2c_msg send_msg;

    /* 准备好发送的数据 */
    write_data[0] = address;
    write_data[1] = data;

    /* 填充发送消息结构体 */
    send_msg.addr = mpu6050_client->addr; // 设备在 i2c 总线上的设备地址（在设备树中设置了）
    send_msg.flags = 0; // 写
    send_msg.buf = write_data; // 需要发送的数据起始地址
    send_msg.len = 2; // 需要发送的数据长度

    ret = i2c_transfer(mpu6050_client->adapter, &send_msg, 1);
    if(ret != 1)
    {
        printk(KERN_DEBUG "%d:%s:transfer i2c data faild!\n", __LINE__, __func__);
        return -1;
    }

    return 0;
}

/** @brief i2c_mpu6050_read
  * @details get hardware resource and make i2c read 
  * @param i2c_client 表示 i2c 从设备
  * @retval 
  * @author lzm
  */
static int i2c_mpu6050_read(struct i2c_client *mpu6050_client, unsigned char address, void *data, unsigned int data_len)
{
    int ret = 0;
    unsigned char data_addr = address;
    struct i2c_msg mpu6050_msg[2];

    mpu6050_msg[0].addr = mpu6050_client->addr;
    mpu6050_msg[0].flags = 0; // write_data
    mpu6050_msg[0].buf = &data_addr;
    mpu6050_msg[0].len = 1;

    mpu6050_msg[1].addr = mpu6050_client->addr;
    mpu6050_msg[1].flags = I2C_M_RD; // read. 该宏可在linux内核源码 i2c_msg 结构体上的注释处可查
    mpu6050_msg[1].buf = data;
    mpu6050_msg[1].len = data_len;

    ret = i2c_transfer(mpu6050_client->adapter, mpu6050_msg, 2);
    if(ret != 2)
    {
        printk(KERN_DEBUG "%d:%s:transfer i2c data faild!\n", __LINE__, __func__);
        return -1;
    }
    return 0;
}

/** @brief mpu6050_init 
  * @param 
  * @retval 
  * @author lzm
  */
static int mpu6050_init(struct i2c_client *mpu6050_client)
{
    int ret = 0;

    /* 配置 */
    ret += i2c_mpu6050_write(mpu6050_client, MPU_PWR_MGMT_1, 0X00);
    ret += i2c_mpu6050_write(mpu6050_client, MPU_SMPLRT_DIV, 0X07);
    ret += i2c_mpu6050_write(mpu6050_client, MPU_CONFIG, 0X06);
    ret += i2c_mpu6050_write(mpu6050_client, MPU_ACCEL_CONFIG, 0X01);

    if(ret < 0)
    {
        printk(KERN_DEBUG "%d:%s:mpu6050 init faild!\n", __LINE__, __func__);
        return -1;
    }

    return 0;
}

/* [drv][1] */
/* 驱动操作接口 */
/** @brief mpu6050_open
  * @param 
  * @retval 
  * @author lzm
  */
static int mpu6050_open(struct inode *inode, struct file *filp)
{
    int ret = 0;

    /* 先获取设备 */
    mpu6050_dev_t *cur_dev = container_of(inode->i_cdev, mpu6050_dev_t, chr_dev);

    /* 校验 (在校验正确前，不得对数据进行修改) */
    if(i2c_get_clientdata(cur_dev->mpu6050_client) != cur_dev)
    {
        printk(KERN_DEBUG "%d:%s:get mpu6050 private data faild!\n", __LINE__, __func__);
        return -1;
    }

    /* 初始化mpu6050 */
    ret = mpu6050_init(cur_dev->mpu6050_client);
    if(ret < 0)
        return -1;

    /* 绑定本设备私人数据到当前打开的设备文件中 */
    filp->private_data = cur_dev;

    return 0;
}

/** @brief mpu6050_release
  * @param 
  * @retval 
  * @author lzm
  */
static int mpu6050_release(struct inode *inode, struct file *filp)
{
    printk(KERN_DEBUG "%d:%s\n", __LINE__, __func__);
    return 0;
}

/** @brief  mpu6050_read
  * @param 
  * @retval 
  * @author lzm
  */
static ssize_t mpu6050_read(struct file *filp, char __user *buf, size_t count, loff_t *ppos)
{
    int ret = 0;
    char data_h;
	char data_l;
	short mpu6050_data[6];
    mpu6050_dev_t *cur_dev = filp->private_data;
    struct i2c_client *mpu6050_client = cur_dev->mpu6050_client;

    /* 校验 (在校验正确前，不得对数据进行修改) */
    if(i2c_get_clientdata(mpu6050_client) != cur_dev)
    {
        printk(KERN_DEBUG "%d:%s:get mpu6050 private data faild!\n", __LINE__, __func__);
        return -1;
    }
	
	i2c_mpu6050_read(mpu6050_client, MPU_ACCEL_XOUT_H, &data_h, 1);
	i2c_mpu6050_read(mpu6050_client, MPU_ACCEL_XOUT_L, &data_l, 1);
	mpu6050_data[0] = data_h << 8;
	mpu6050_data[0] += data_l;

	i2c_mpu6050_read(mpu6050_client, MPU_ACCEL_YOUT_H, &data_h, 1);
	i2c_mpu6050_read(mpu6050_client, MPU_ACCEL_YOUT_L, &data_l, 1);
	mpu6050_data[1] = data_h << 8;
    mpu6050_data[1] += data_l;

	i2c_mpu6050_read(mpu6050_client, MPU_ACCEL_ZOUT_H, &data_h, 1);
	i2c_mpu6050_read(mpu6050_client, MPU_ACCEL_ZOUT_L, &data_l, 1);
	mpu6050_data[2] = data_h << 8;
	mpu6050_data[2] += data_l;

	i2c_mpu6050_read(mpu6050_client, MPU_GYRO_XOUT_H, &data_h, 1);
	i2c_mpu6050_read(mpu6050_client, MPU_GYRO_XOUT_L, &data_l, 1);
	mpu6050_data[3] = data_h << 8;
	mpu6050_data[3] += data_l;

	i2c_mpu6050_read(mpu6050_client, MPU_GYRO_YOUT_H, &data_h, 1);
	i2c_mpu6050_read(mpu6050_client, MPU_GYRO_YOUT_L, &data_l, 1);
	mpu6050_data[4] = data_h << 8;
	mpu6050_data[4] += data_l;

	i2c_mpu6050_read(mpu6050_client, MPU_GYRO_ZOUT_H, &data_h, 1);
	i2c_mpu6050_read(mpu6050_client, MPU_GYRO_ZOUT_L, &data_l, 1);
	mpu6050_data[5] = data_h << 8;
	mpu6050_data[5] += data_l;

    printk("AX=%d, AY=%d, AZ=%d \n",(int)mpu6050_data[0],(int)mpu6050_data[1],(int)mpu6050_data[2]);
	printk("GX=%d, GY=%d, GZ=%d \n \n",(int)mpu6050_data[3],(int)mpu6050_data[4],(int)mpu6050_data[5]);

	/* 将读取得到的数据拷贝到用户空间 */
	ret = copy_to_user(buf, mpu6050_data, MIN(6, count));
	if(ret != 0)
	{
        printk(KERN_DEBUG "%d:%s:get data faild!\n", __LINE__, __func__);
		return -1;
	}    
    return ret;
}

/* [drv][2] */
/* 填充驱动操作结构体 */
static struct file_operations i2c_mpu6050_fops = 
{
    .owner = THIS_MODULE,
    .open  = mpu6050_open,
    .release = mpu6050_release,
    .read  = mpu6050_read,
};

/* [bus_drv][1] */
/* 总线 驱动 */
/** @brief mpu6050_probe
  * @details 匹配后的函数 probe函数
  * @param i2c_client 表示 i2c 从设备
  * @param i2c_device_id 要匹配的从设备信息
  * @retval 
  * @author lzm
  */
static int mpu6050_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
    int ret = 0;
    mpu6050_dev_t *cur_dev = NULL;

    printk(KERN_ERR "%d:%s\n", __LINE__, __func__);

    /* 为当前设备申请申请内存 */
    cur_dev = devm_kzalloc(&client->dev, sizeof(mpu6050_dev_t), GFP_KERNEL);
    if(cur_dev == NULL)
    {
        printk("%d:%s:malloc faild!\n", __LINE__, __func__);
        return -ENOMEM;
    }

    /* [drv] 申请设备号 */
    ret = alloc_chrdev_region(&cur_dev->dev_num, 0, 1, MPU6050_DEV_NAME);
    if(ret < 0)
    {
        printk("%d:%s:alloc char device num faild!\n", __LINE__, __func__);
        return -1;
    }
    /* [drv] 初始化设备内核文件，并绑定操作接口 */
    cdev_init(&cur_dev->chr_dev, &i2c_mpu6050_fops);

    /* [drv] 把设备内核文件注册到内核，并绑定设备号 */
    ret = cdev_add(&cur_dev->chr_dev, cur_dev->dev_num, 1);
    if(ret < 0)
    {
        printk("%d:%s:char device file add kernel faild!\n", __LINE__, __func__);
        unregister_chrdev_region(cur_dev->dev_num, 1);
        return -1;
    }

    id_num++;

    /* [drv] 创建设备类 */
    // 共用一个设备类，所以在内核模块入口中创建
    if(mpu6050_dev_class == NULL)
    {
        mpu6050_dev_class = class_create(THIS_MODULE, MPU6050_DEV_CLASS);
    }

    /* [drv] 创建设备节点，并绑定设备号 */
    device_create(mpu6050_dev_class, NULL, cur_dev->dev_num, NULL, MPU6050_DEV_INODE_NAME"_%d", id_num);

    /* 绑定一下i2c 从设备 */
    cur_dev->mpu6050_client = client;

    /* 或许需要添加到驱动私有数据 */
    i2c_set_clientdata(client, cur_dev);

    return 0;
}

/** @brief mpu6050_remove
  * @details 移除函数
  * @param i2c_client 表示 i2c 从设备
  * @retval 
  * @author lzm
  */
static int mpu6050_remove(struct i2c_client *client)
{
    mpu6050_dev_t *cur_dev = NULL;

    printk(KERN_ERR "%d:%s\n", __LINE__, __func__);

    cur_dev = i2c_get_clientdata(client);
    if(cur_dev == NULL)
    {
        printk(KERN_ERR "%d:%s:get cur dev faild!\n", __LINE__, __func__);
        return -1;
    }

    /* [drv] 删除设备节点 */
    device_destroy(mpu6050_dev_class, cur_dev->dev_num);

    /* 删除设备类 */
    // 在模块退出时再删除

    /* [drv] 删除设备内核文件 */
    cdev_del(&cur_dev->chr_dev);

    /* [drv] 归还设备号 */
    unregister_chrdev_region(cur_dev->dev_num, 1);

    return 0;
}

/* [bus_drv][2] */
/* 兼容性 匹配 */
/* 普通兼容匹配表（支持设备树），不对设备商字段匹配，且匹配优先级最低 */
static const struct i2c_device_id mpu6050_device_id[] =
{
    {"lzm,i2c_mpu6050",0},
    {}
};

/* 设备树匹配表，全字段进行匹配。优先级高于 .id_table */
static const struct of_device_id mpu6050_of_match_table[] = 
{
    {.compatible = "lzm,i2c_mpu6050"},
    {}
};

/* [bus_drv][3] */
/* 填充 i2c 总线设备驱动 结构体 */
struct i2c_driver mpu6050_driver = {
    .probe  = mpu6050_probe,
    .remove = mpu6050_remove,
    .id_table = mpu6050_device_id,
    .driver = {
        .name = "lzm,i2c_mpu6050", // 注意，有匹配的可能性
        .owner = THIS_MODULE,
        .of_match_table = mpu6050_of_match_table,
    }
};

/* [module][1] */
/* 模块入口函数 */
static __init int i2c_mpu6050_drv_init(void)
{
    int ret;

    printk(KERN_ERR "%d:%s\n", __LINE__, __func__);

    /* [drv] 创建设备类 */
    mpu6050_dev_class = class_create(THIS_MODULE, MPU6050_DEV_CLASS);

    /* [bus_drv] 添加i2c驱动到总线 */
    ret = i2c_add_driver(&mpu6050_driver);

    return ret;
}
module_init(i2c_mpu6050_drv_init);

/* [module][2] */
/* 模块出口函数 */
static __exit void i2c_mpu6050_drv_exit(void)
{
    printk(KERN_ERR "%d:%s\n", __LINE__, __func__);

   /* [bus_drv] 从总线中移除驱动i2c总线驱动 */ 
   i2c_del_driver(&mpu6050_driver);

    /* [drv] 删除设备类 */
   class_destroy(mpu6050_dev_class);
}
module_exit(i2c_mpu6050_drv_exit);

/* [module][3] */
/* 协议 */
MODULE_LICENSE("GPL");
MODULE_AUTHOR("lizhuming");
MODULE_DESCRIPTION("this is a i2c driver for mpu6050!");

